#include <unistd.h>
#include <iostream>
#include <cassert>
#include <string>
#include <cstdio>
#include <vector>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

#define MAKE_RAND() srand((size_t)time(nullptr) ^ 15423 ^ 12)
///////////////////////子进程执行的任务////////////////////////////////////////
void IO_mission()
{
    std::cout << getpid() << ":执行IO任务\n" << std::endl;
    sleep(1);
}

void flush_mission()
{
    std::cout << getpid() << ":执行刷新任务\n" << std::endl;
    sleep(1);
}

void download_mission()
{
    std::cout << getpid() << ":执行下载任务\n" << std::endl;
    sleep(1);
}

typedef void(*func) ();

void loadSubMission(std::vector<func>& vec_mis)   
{
    vec_mis.push_back(IO_mission);
    vec_mis.push_back(flush_mission);
    vec_mis.push_back(download_mission);
}

///////////////////////////////////////////////////////////////////////////////////
struct Information
{
    Information(int writeFd,int subId)
        :_writeFd(writeFd),_subId(subId)
    {
        char buffer[1024] = {0};
        snprintf(buffer,sizeof(buffer),"process%d[pid(%d)-writeFd(%d)]",_size++,_subId,_writeFd);
        _name = buffer; 
    }
    std::string _name;
    int _writeFd;   // 管道写端文件描述符
    pid_t _subId; // 子进程的pid
    static int _size;
};
int Information::_size = 1;

// 从管道读取信号
int readMisson(int fd)
{
    int sig = 0;
    int ret = read(fd,&sig,sizeof(int));
    if(ret > 0)
    {
        return sig;
    }
    else 
    {
        return -1;
    }
}

// 创建管道和子进程
void createSubprocess(int subSize,std::vector<Information>& vec_info,std::vector<func>& vec_mis)
{
    std::vector<int> deleteFd;  //保存管道写端的文件描述符
    for(int i=0;i<subSize;i++)
    {
        int fds[2] = {0};
        int n = pipe(fds);
        assert(n == 0);

        deleteFd.push_back(fds[1]); //保存写端

        pid_t id = fork();
        assert(id >= 0);

        if(id == 0) // 子进程
        {
            // 处理任务之前，子进程关掉所有管道写端
            for(auto& e:deleteFd)
            {
                close(e);
            }

            while(true)
            {
                int sig = readMisson(fds[0]);
                if(sig >= 0 && sig < vec_mis.size())
                {
                    vec_mis[sig]();
                }
                else if(sig == -1)
                {
                    break;
                }
            }

            close(fds[0]);
            exit(0);
        }

        close(fds[0]);

        // 保存每个管道的读端,每个子进程pid
        vec_info.push_back(Information(fds[1],id));

    }
}


// 向管道发送"控制信号"
void sendControlSig(std::vector<Information>& vec_info,std::vector<func>& vec_mis)
{
    int subSize = vec_info.size();  // 子进程数量
    int misSize = vec_mis.size();   // 任务数量
    
    int cnt = 4;
    while(cnt--)
    {
        // 随机选取管道、任务
        int subIndex = rand() % subSize;
        int misIndex = rand() % misSize;
        
        // 发送任务下标
        std::cout << misIndex << "号任务发送给" << vec_info[subIndex]._name << std:: endl;
        write(vec_info[subIndex]._writeFd,&misIndex,sizeof(int));
        sleep(1);
    }

    for(auto& e:vec_info)
    {
        close(e._writeFd);

        // 本来在这里可以直接等待子进程退出
        // 但是这么写会存在bug，非常严重的bug
        waitpid(e._subId,nullptr,0);
        std::cout << e._name << "已被回收" << std::endl;
    }
}


// 等待子进程退出
void waitSubExit(std::vector<Information>& vec_info)
{
    for(int i=vec_info.size()-1;i>=0;i--)
    {
        int ret = waitpid(vec_info[i]._subId,nullptr,0);
        assert(ret == vec_info[i]._subId);

        std::cout << vec_info[i]._name << "已被回收" << std::endl;
    }
}


int main(int argc,char* argv[]) // 我们希望从命令行获取子进程数量
{
    MAKE_RAND();
    if(argc < 2)
    {
        std::cout << "命令行参数不够!" << std::endl;
        return -1;
    }
    int subSize = *(argv[1]) - '0';


    std::vector<Information> vec_Information;    // 存储管道、子进程信息的容器

    std::vector<func> vec_mission;  //存储子进程执行的任务的容器
    loadSubMission(vec_mission);    // 将子进程要执行的任务加载到容器中

    createSubprocess(subSize,vec_Information,vec_mission);
    sendControlSig(vec_Information,vec_mission);
    //waitSubExit(vec_Information);
    return 0;
}